package com.github.llyb120.ctrl;

import cn.hutool.core.util.HexUtil;
import com.antherd.smcrypto.sm2.Keypair;
import com.antherd.smcrypto.sm2.Sm2;
import com.antherd.smcrypto.sm3.Sm3;
import com.cqp.sm9utilsstarter.SM9.curve.SM9Curve;
import com.cqp.sm9utilsstarter.SM9.result.ResultCipherText;
import com.cqp.sm9utilsstarter.SM9.service.SM9Service;
import com.cqp.sm9utilsstarter.SM9.service.SM9ServiceImpl;
import com.github.llyb120.crypt.Util;
import com.github.llyb120.crypt.sm4.SM4Utils;
import com.github.llyb120.crypt.zuc.ZUC;
import com.github.llyb120.json.Obj;
import com.github.llyb120.request.JavaSourceCodeRequest;
import org.gmssl.GmSSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;

import static com.github.llyb120.json.Json.o;

/**
 * @Author: Administrator
 * @Date: 7/27/2021 11:45 AM
 */
@CrossOrigin
@RestController
@RequestMapping("/api")
public class TestController {

//
//    @RequestMapping("test")
//    public Object test(
//            @RequestBody JavaSourceCodeRequest request
//    ) {
//        return javaCodeRunService.runCode(request);
//    }


//    @RequestMapping("test2")
//    public void test2(){
//        String code = "String before = \"abcdefg\";\n" +
//            "log(\"will changed\");\n" +
//            "//#dump(before)\n" +
//            "String after = \"hijklmn\";\n" +
//            "log(\"has been changed\");\n" +
//            "//#dump(after)\n";
//
//        DynamicJavaRunner.run(DynamicCode.builder().mainCode(code).build());
//    }


    @RequestMapping("/sm2/doEncrypt")
    public Object doEncrypt(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String pubk = obj.ss("publicKey");
//        String prik = obj.ss("privateKey");
        String text = obj.ss("text");
        String cipherText = Sm2.doEncrypt(text, pubk);//SM2EncDecUtils.encrypt(Util.hexToByte(pubk), sourceData);
        //cipherText = "0452ba81cf5119c9f29c81c2be9c4a49ad8c0a33ed899b60548d21a62971a8e994cafc0e9fbc710a0a220b055804bb890833b50ac04ec4e130a5db75338c0c1d49a52a6d373076a5db370564a5cebb5300f79877003c52adf49dac16370e51e14e0754110547bb3b";
        return o(
                "result", cipherText
        );
//        System.out.println(cipherText);
//        System.out.println("解密: ");
//        plainText = new String(SM2EncDecUtils.decrypt(Util.hexToByte(prik), Util.hexToByte(cipherText)));
//        System.out.println(plainText);
    }

    @RequestMapping("/sm2/doSign")
    public Object doSm2Sign(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
//        String pubk = obj.ss("");
        String prik = obj.ss("privateKey");
        String text = obj.ss("text");
        String res = Sm2.doSignature(text, prik);
        return o("result", res);
    }

    @RequestMapping("/sm2/doVertify")
    public Object doSm2Vertify(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String pubk = obj.ss("publicKey");
//        String prik = obj.ss("privateKey");
        String text = obj.ss("signSource");
        boolean res = Sm2.doVerifySignature(text, obj.ss("signText"), pubk);
        return o("result", String.valueOf(res));
    }

    @RequestMapping("/sm2/doDecrypt")
    public Object doDecrypt(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String prik = obj.ss("privateKey");
        String text = obj.ss("text");
        String plainText = Sm2.doDecrypt(text, prik);
        return o(
                "result", plainText
        );
    }


//    static GmSSL gmssl = new GmSSL();

    @RequestMapping("/sm3/doEncrypt")
    public Object doSm3Encrypt(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String key = obj.ss("key");
        String text = obj.ss("text");
        String result = Sm3.sm3(text);
//        byte[] dgst = gmssl.digest("SM3", text.getBytes());

//        KeyParameter keyParameter = new KeyParameter(key.getBytes());
//        SM3Digest digest = new SM3Digest();
//        HMac mac = new HMac(digest);
//        mac.init(keyParameter);
//        mac.update(text.getBytes(), 0, text.getBytes().length);
//        byte[] result = new byte[mac.getMacSize()];
//        mac.doFinal(result, 0);
        return o(
                "result", result//new String(result)
        );
    }

    @RequestMapping("/sm4/ecb/doDecrypt")
    public Object doSm4Decrypt(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String key = obj.ss("key");
        String text = obj.ss("text");
        SM4Utils sm4 = new SM4Utils();
        sm4.secretKey = key;//"E76E9B4E0245BC56FCE4E29B208C6A50";
        sm4.hexString = true;
        String plainText = sm4.decryptData_ECB(text);
        return o(
                "result", plainText
        );
    }

    @RequestMapping("/sm4/ecb/doEncrypt")
    public Object doSm4Encrypt(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String key = obj.ss("key");
        String text = obj.ss("text");
        String plainText = text;
        String s = Util.byteToHex(plainText.getBytes());
        System.out.println("原文" + s);
        SM4Utils sm4 = new SM4Utils();
        sm4.secretKey = key;//"E76E9B4E0245BC56FCE4E29B208C6A50";
        sm4.hexString = true;
        System.out.println("ECB模式加密");
        String cipherText = sm4.encryptData_ECB(plainText);
        if (cipherText == null) {
            throw new RuntimeException("KEY错误");
        }
        System.out.println("密文: " + cipherText);
        return o(
                "result", cipherText
        );
//        plainText = sm4.decryptData_ECB(cipherText);
//        System.out.println("明文: " + plainText);
//
//
//        System.out.println("CBC模式加密");
//        sm4.iv = "30303030303030303030303030303030";
//        cipherText = sm4.encryptData_CBC(plainText);
//        System.out.println("加密密文: " + cipherText);
//        System.out.println("");
//        plainText = sm4.decryptData_CBC(cipherText);
//        System.out.println("解密明文: " + plainText);
    }


    @RequestMapping("/sm4/cbc/doEncrypt")
    public Object doSm4Encrypt1(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String key = obj.ss("key");
        String text = obj.ss("text");
        String plainText = text;
        String s = Util.byteToHex(plainText.getBytes());
        System.out.println("原文" + s);
        SM4Utils sm4 = new SM4Utils();
        sm4.secretKey = key;//"E76E9B4E0245BC56FCE4E29B208C6A50";
        sm4.hexString = true;
        sm4.iv = obj.ss("iv");//"30303030303030303030303030303030";
        String cipherText = sm4.encryptData_CBC(plainText);
        if (cipherText == null) {
            throw new RuntimeException("KEY错误");
        }
        System.out.println("密文: " + cipherText);
        return o(
                "result", cipherText
        );
    }

    @RequestMapping("/sm4/cbc/doDecrypt")
    public Object doSm4Decrypt1(@RequestBody Map body) throws IOException {
        Obj obj = o(body);
        String key = obj.ss("key");
        String text = obj.ss("text");
        String plainText = text;
        SM4Utils sm4 = new SM4Utils();
        sm4.secretKey = key;//"E76E9B4E0245BC56FCE4E29B208C6A50";
        sm4.hexString = true;
        sm4.iv = obj.ss("iv");//"30303030303030303030303030303030";
        String cipherText = sm4.encryptData_CBC(plainText);
        if (cipherText == null) {
            throw new RuntimeException("KEY错误");
        }
        return o(
                "result", cipherText
        );
    }

    SM9Service sm9Service = new SM9ServiceImpl();

    @RequestMapping("/sm9/doEncrypt")
    public Object doSm9Encrypt(@RequestBody Map body) throws Exception {
        Obj obj = o(body);
        String id_B = obj.ss("key"); //Bob
        String msg = obj.ss("text");//"Chinese IBE standard";
        ResultCipherText result = sm9Service.encrypt(id_B, msg);
//        SM9Curve sm9Curve = new SM9Curve();
//        KGC kgc = new KGC(sm9Curve);
//        SM9 sm9 = new SM9(sm9Curve);
//        MasterKeyPair encryptMasterKeyPair = kgc.genEncryptMasterKeyPair();
//        System.out.println(encryptMasterKeyPair.toString());
//        PrivateKey encryptPrivateKey=kgc.getPrivateKey(encryptMasterKeyPair.getPrivateKey(), id_B);
//        System.out.println("-------");
//        System.out.println(encryptPrivateKey.toString());
//        int macKeyByteLen = 32;
//        boolean isBaseBlockCipher = false;
//        ResultCipherText resultCipherText = sm9.encrypt(encryptMasterKeyPair.getPublicKey(), id_B, msg.getBytes(), isBaseBlockCipher, macKeyByteLen);
//        System.out.println("加密后的密文 C=C1||C3||C2:");
//        System.out.println(SM9Utils.toHexString(resultCipherText.toByteArray()));
//        byte[] msgd = sm9.decrypt(resultCipherText, encryptPrivateKey, id_B, isBaseBlockCipher, macKeyByteLen);
//        System.out.println("解密后的明文M':");
//        System.out.println(new String(msgd));
        return o(
                "result", Util.byteToHex(result.toByteArray())
        );
    }


    @RequestMapping("/sm9/doDecrypt")
    public Object doSm9Decrypt(@RequestBody Map body) throws Exception {
        Obj obj = o(body);
        String id_B = obj.ss("key"); //Bob
        String msg = obj.ss("text");//"Chinese IBE standard";
//        SM9Curve sm9Curve
        Field field = SM9ServiceImpl.class.getDeclaredField("sm9Curve");
        field.setAccessible(true);
        SM9Curve val = (SM9Curve) field.get(sm9Service);
        ResultCipherText resultCipherText = ResultCipherText.fromByteArray(val, Util.hexToByte(msg));
        String msgd = sm9Service.decrypt(id_B, resultCipherText);
//        SM9Curve sm9Curve = new SM9Curve();
//        KGC kgc = new KGC(sm9Curve);
//        SM9 sm9 = new SM9(sm9Curve);
//        MasterKeyPair encryptMasterKeyPair = kgc.genEncryptMasterKeyPair();
//        System.out.println(encryptMasterKeyPair.toString());
//        PrivateKey encryptPrivateKey=kgc.getPrivateKey(encryptMasterKeyPair.getPrivateKey(), id_B);
//        System.out.println("-------");
//        System.out.println(encryptPrivateKey.toString());
//        int macKeyByteLen = 32;
//        boolean isBaseBlockCipher = false;
////        ResultCipherText resultCipherText = sm9.encrypt(encryptMasterKeyPair.getPublicKey(), id_B, msg.getBytes(), isBaseBlockCipher, macKeyByteLen);
////        System.out.println("加密后的密文 C=C1||C3||C2:");
////        System.out.println(SM9Utils.toHexString(resultCipherText.toByteArray()));
//        ResultCipherText resultCipherText = ResultCipherText.fromByteArray(sm9Curve, Util.hexToByte(msg));
//        byte[] msgd = sm9.decrypt(resultCipherText, encryptPrivateKey, id_B, isBaseBlockCipher, macKeyByteLen);
//        System.out.println("解密后的明文M':");
//        System.out.println(new String(msgd));
        return o(
                "result", (msgd)
        );
    }

    @RequestMapping("/zuc/doEncrypt")
    public Object doZucEncrypt(@RequestBody Map body) throws Exception {
        Obj obj = o(body);
        String msg = obj.ss("text");//"Chinese IBE standard";
        String ivStr = obj.ss("iv");
        String keyStr = obj.ss("key");
//        int iv[] = { 0x84, 0x31, 0x9a, 0xa8, 0xde, 0x69, 0x15, 0xca, 0x1f, 0x6b, 0xda, 0x6b, 0xfb, 0xd8, 0xc7, 0x66 };
//        int key[] = { 0x3d,0x4c,0x4b,0xe9,0x6a,0x82,0xfd,0xae,0xb5,0x8f,0x64,0x1d,0xb1,0x7b,0x45,0x5b };
        int keyStreamSize = obj.i("keyStreamSize", 2);
        long[] pKeyStream = new long[keyStreamSize];
        ZUC zuc = new ZUC();
        int[] iv = new int[16];
        int[] key = new int[16];
        for (int i = 0; i < 16; i++) {
            String str = keyStr.substring(i*2,i*2+2);
            key[i] = HexUtil.toBigInteger(str).intValue();
            str = ivStr.substring(i*2,i*2+2);
            iv[i] = HexUtil.toBigInteger(str).intValue();
        }
        zuc.Initialization(key, iv);
        zuc.GenerateKeyStream(pKeyStream, keyStreamSize);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < keyStreamSize; ++i) {
            sb.append(String.format("%x", pKeyStream[i]).toUpperCase());
        }
//        SM9Curve sm9Curve = new SM9Curve();
//        KGC kgc = new KGC(sm9Curve);
//        SM9 sm9 = new SM9(sm9Curve);
//        MasterKeyPair encryptMasterKeyPair = kgc.genEncryptMasterKeyPair();
//        System.out.println(encryptMasterKeyPair.toString());
//        PrivateKey encryptPrivateKey=kgc.getPrivateKey(encryptMasterKeyPair.getPrivateKey(), id_B);
//        System.out.println("-------");
//        System.out.println(encryptPrivateKey.toString());
//        int macKeyByteLen = 32;
//        boolean isBaseBlockCipher = false;
//        ResultCipherText resultCipherText = sm9.encrypt(encryptMasterKeyPair.getPublicKey(), id_B, msg.getBytes(), isBaseBlockCipher, macKeyByteLen);
//        System.out.println("加密后的密文 C=C1||C3||C2:");
//        System.out.println(SM9Utils.toHexString(resultCipherText.toByteArray()));
//        byte[] msgd = sm9.decrypt(resultCipherText, encryptPrivateKey, id_B, isBaseBlockCipher, macKeyByteLen);
//        System.out.println("解密后的明文M':");
//        System.out.println(new String(msgd));
        return o(
                "result", sb.toString()//Util.byteToHex(result.toByteArray())
        );
    }

    static GmSSL gmSSL = new GmSSL();

    @RequestMapping("/zuc/doEncrypt2")
    public Object doZucEncrypt2(@RequestBody Map body){
        String text = o(body).ss("text");
        byte[] key = HexUtil.decodeHex("E76E9B4E0245BC56FCE4E29B208C6A50");
        byte[] iv = HexUtil.decodeHex("30303030303030303030303030303030");
        byte[] res = gmSSL.symmetricEncrypt("zuc", text.getBytes(), key, iv);
        return o(
                "result", HexUtil.encodeHexStr(res)//Util.byteToHex(result.toByteArray())
        );
    }

    @RequestMapping("/zuc/doDecrypt2")
    public Object doZucDecrypt2(@RequestBody Map body){
        String text = o(body).ss("text");
        byte[] key = HexUtil.decodeHex("E76E9B4E0245BC56FCE4E29B208C6A50");
        byte[] iv = HexUtil.decodeHex("30303030303030303030303030303030");
        byte[] res = gmSSL.symmetricDecrypt("zuc", HexUtil.decodeHex(text), key, iv);
        return o(
                "result", new String(res)//Util.byteToHex(result.toByteArray())
        );
    }

    @RequestMapping("/sm2/generatePair")
    public Object generatePair() {
        Keypair pair = Sm2.generateKeyPairHex();//SecureUtil.generateKeyPair("SM2");
//        SM2KeyVO keypair = SM2EncDecUtils.generateKeyPair();//.getPubHexInSoft()
        return o(
                "pri", pair.getPrivateKey().toLowerCase(),
                "pub", pair.getPublicKey().toLowerCase()
//                "pub", keypair.getPubHexInSoft().toUpperCase()
        );
    }


    /**
     * int到byte[] 由高位到低位
     *
     * @param i 需要转换为byte数组的整行值。
     * @return byte数组
     */
    private byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        result[0] = (byte) ((i >> 24) & 0xFF);
        result[1] = (byte) ((i >> 16) & 0xFF);
        result[2] = (byte) ((i >> 8) & 0xFF);
        result[3] = (byte) (i & 0xFF);
        return result;
    }

    private byte[] intArrayToByteArray(int[] ints) {
        byte[] result = new byte[ints.length * 4];
        for (int i = 0; i < ints.length; i++) {
            byte[] bs = intToByteArray(ints[i]);
            System.arraycopy(bs, 0, result, i * 4, 4);
        }
        return result;
    }

    /**
     * byte[]转int
     *
     * @param bytes 需要转换成int的数组
     * @return int值
     */
    public int byteArrayToInt(byte[] bytes) {
        int value = 0;
        for (int i = 0; i < 4; i++) {
            int shift = (3 - i) * 8;
            value += (bytes[i] & 0xFF) << shift;
        }
        return value;
    }

    private int[] byteArrayToIntArray(byte[] bytes) {
        byte[] swap = new byte[4];
        int[] ints = new int[16];
        for (int i = 0; i < 16; i++) {
            System.arraycopy(bytes, i * 4, swap, 0, 4);
            ints[i] = byteArrayToInt(swap);
        }
        return ints;
    }
}
